﻿using System;
using System.Collections.Generic;
using System.Linq;
using IQIGame.Onigao.Framework;
using IQIGame.Onigao.GamePlay;
using LFloatMath.Math;
using NetProtocol.POD;

namespace IQIGame.Onigao.Logic
{
    public static class UnitFactory
    {
#region Create POD

        public static BattleUnitPOD CreateHeroPOD(int heroID, int skinID, int[] attr, Dictionary<int, int> skills, int level)
        {
            var rConfig = TableCenter.hero.Get(heroID);
            var rUnitPOD = new BattleUnitPOD()
            {
                configID = heroID,
                skinID = skinID,
                attrs = attr.Clone<int>(),
                unsafe_initBuff = new List<int>(),
                unsafe_skills = new Dictionary<int, int>(skills),
                level = level,
                unitType = (int)EUnitType.Hero,
                radius = rConfig.Radius,
                ai = rConfig.AI
            };
            return rUnitPOD;
        }

        public static BattleTeamPOD CreateMonsterTeamPOD(int monsterTeamID)
        {
            var rList = new List<BattleUnitPOD>();
            var rPOD = new BattleTeamPOD()
            {
                teamIndex = BattleConst.TeamBlueIndex,
                monsterTeamID = monsterTeamID,
                unsafe_units = rList,
            };
            var cfgMonsterTeam = TableCenter.monsterTeam.Get(monsterTeamID);
            for (int j = 0; j < cfgMonsterTeam.MonsterId.Count; j++)
            {
                if (cfgMonsterTeam.MonsterId[j] == 0)
                {
                    continue;
                }
                var rUnitPOD = UnitFactory.CreateMonsterPOD(cfgMonsterTeam.MonsterId[j], cfgMonsterTeam.MonsterLv[j], cfgMonsterTeam.MonsterPower[j],
                    cfgMonsterTeam.MonsterPowerType[j], cfgMonsterTeam.MonsterAttrCorr[j]);
                rUnitPOD.formationIndex = j;
                rList.Add(rUnitPOD);
            }
            return rPOD;
        }

        public static BattleUnitPOD CreateMonsterPOD(int monsterID, int level, int power, int powerType, IReadOnlyList<int> attrCorr)
        {
            var rConfig = TableCenter.monster.Get(monsterID);
            var rAttrType = new List<int>();
            var rAttrValue = new List<int>();
            UnitFactory.GetMonsterPanelAttr(power, powerType, attrCorr, ref rAttrType, ref rAttrValue, out int nWeakNum);
            int[] rAttrArr = new int[TableCenter.attribute.DataList.Count + 1];
            for (int attrIndex = 0; attrIndex < rAttrType.Count; attrIndex++)
            {
                rAttrArr[rAttrType[attrIndex]] = rAttrValue[attrIndex];
            }
            rAttrArr[(int)EAttributeType.Hp] = rAttrArr[(int)EAttributeType.MaxHp];
            rAttrArr[(int)EAttributeType.SP] = rAttrArr[(int)EAttributeType.InitSP];
            var rSkill = new Dictionary<int, int>(rConfig.Skill.Count);
            for (int skillIndex = 0; skillIndex < rConfig.Skill.Count; skillIndex++)
            {
                rSkill.Add(rConfig.Skill[skillIndex], 1); // 默认1级
            }
            int weaknessType = 0;
            for (int k = 0; k < rConfig.WeakType.Count; k++)
            {
                weaknessType |= 1 << rConfig.WeakType[k];
            }
            var rUnitPOD = new BattleUnitPOD()
            {
                configID = monsterID,
                pid = 0,
                attrs = rAttrArr,
                unsafe_skills = rSkill,
                unsafe_initBuff = new List<int>(),
                level = level,
                weaknessPoint = nWeakNum,
                unitType = (int)EUnitType.Monster,
                radius = rConfig.Radius,
                ai = rConfig.AI,
                weaknessType = weaknessType,
                unsafe_flankAngle = new List<int>(rConfig.SideAngle),
                unsafe_backAngle = new List<int>(rConfig.BackAngle),
            };
            return rUnitPOD;
        }

#endregion

#region Create Unit

        public static Unit CreateBattleUnit(Battle battle, int nTeamIndex, BattleUnitPOD rPOD, bool bDefaultAI)
        {
            var rSceneConfig = TableCenter.battleScene.Get(battle.playCtrl.battleSceneID);
            var rPosList = nTeamIndex == BattleConst.TeamRedIndex ? rSceneConfig.PlayerBorn[rPOD.formationIndex] : rSceneConfig.MonsterBorn[rPOD.formationIndex];
            var rBornPos = new LVector2(rPosList[0].ToLFloat(), rPosList[1].ToLFloat());
            var rBornForward = nTeamIndex == BattleConst.TeamRedIndex ? BattleConst.SelfForward : BattleConst.EnemyForward;
            var rUnit = CreateBattleUnit(battle, rPOD.configID, nTeamIndex, (EUnitType)rPOD.unitType, rPOD.radius.ToLFloat() / LFloat.hundred, rBornPos, rBornForward,
                rPOD.attrs, rPOD.skills, bDefaultAI, rPOD.ai, rPOD.weaknessPoint, (EDamageType)rPOD.weaknessType, rPOD.flankAngle, rPOD.backAngle, rPOD.skinID, rPOD.pet);
            rUnit.unitPOD = rPOD;
            return rUnit;
        }

        public static Unit CreateBattleUnit(Battle battle, int configID, int teamIndex, EUnitType unitType, LFloat radius, LVector2 bornPos, LVector2 forward,
            IReadOnlyList<int> attrType, IReadOnlyList<int> attrValue, IReadOnlyList<int> skills, bool bDefaultAI, string aiPath, int nMaxWeaknessPoint, EDamageType rWeaknessType,
            List<int> flankAngle, List<int> backAngle, int nSkinID = 0, int petID = 0)
        {
            var rAttrArr = new int[(int)EAttributeType.Max];
            var nLen = Math.Min(attrType.Count, attrValue.Count);
            for (int i = 0; i < nLen; i++)
            {
                rAttrArr[attrType[i] - 1] = attrValue[i];
            }
            var rSkillDict = new Dictionary<int, int>();
            for (int i = 0; i < skills.Count; i++)
            {
                rSkillDict.TryAdd(skills[i], 1);
            }
            return CreateBattleUnit(battle, configID, teamIndex, unitType, radius, bornPos, forward, rAttrArr, rSkillDict, bDefaultAI, aiPath, nMaxWeaknessPoint, rWeaknessType,
                flankAngle, backAngle, nSkinID, petID);
        }

        public static Unit CreateBattleUnit(Battle battle, int configID, int teamIndex, EUnitType unitType, LFloat radius, LVector2 bornPos, LVector2 forward, int[] attrValue,
            Dictionary<int, int> skills, bool defaultAI, string aiPath, int maxWeaknessPoint, EDamageType weaknessType, List<int> flankAngle = null, List<int> backAngle = null, int nSkinID = 0, int petID = 0)
        {
            var rUnit = new Unit()
            {
                GUID = battle.CreateID,
                battle = battle,
                teamIndex = teamIndex,
                configID = configID,
                unitType = unitType,
                skinID = nSkinID,
                bornPos = bornPos,
            };
            rUnit.radius = radius; // 厘米转米

            rUnit.AddComponent<BuffComponent>(ETComponentType.BuffComponent);
            var rAttrComp = rUnit.AddComponent<AttributeComponent>(ETComponentType.AttributeComponent);
            rUnit.AddComponent<CustomPropertyComponent>(ETComponentType.CustomPropertyComponent);
            var rAIComp = rUnit.AddComponent<AIComponent>(ETComponentType.AIComponet);
            var rSkillComp = rUnit.AddComponent<SkillComponent>(ETComponentType.SkillComponent);
            var rColComp = rUnit.AddComponent<CollisionComponent>(ETComponentType.CollisionComponent);
            rUnit.AddComponent<ChargeComponent>(ETComponentType.ChargeComponent);
            var rPetComp = rUnit.AddComponent<PetComponent>(ETComponentType.PetComponent);

            rAttrComp.Initialize(attrValue);
            rSkillComp.Initialize(skills);
            rColComp.InitializeCircle(rUnit.radius);
            rAIComp.Initialize(defaultAI, aiPath);
            rPetComp.Initialize(petID);

            // 初始化不检测位置
            rUnit.SetPos(bornPos, forward, null, false);
            rUnit.maxWeaknessPoint = maxWeaknessPoint;
            rUnit.weaknessType = weaknessType;

            int flankLeft = BattleConst.CommonFlankRange;
            int flankRight = BattleConst.CommonFlankRange;
            int backLeft = BattleConst.CommonBackRange;
            int backRight = BattleConst.CommonBackRange;
            if (flankAngle != null && flankAngle.Count > 0)
            {
                flankLeft = flankAngle[0];
                flankRight = flankAngle[1];
            }
            if (backAngle != null && backAngle.Count > 0)
            {
                backLeft = backAngle[0];
                backRight = backAngle[1];
            }

            rUnit.backstabRange[0] = flankLeft.ToLFloat() * LMath.Deg2Rad;
            rUnit.backstabRange[1] = flankRight.ToLFloat() * LMath.Deg2Rad;
            rUnit.backstabRange[2] = backLeft.ToLFloat() * LMath.Deg2Rad;
            rUnit.backstabRange[3] = backRight.ToLFloat() * LMath.Deg2Rad;
            return rUnit;
        }

        public static Totem CreateBattleTotem(Battle battle, Unit summoner, LVector2 bornPos, int duration)
        {
            var rTotem = new Totem()
            {
                GUID = battle.CreateID,
                battle = battle,
                summoner = summoner,
                teamIndex = summoner.teamIndex,
                unitType = EUnitType.Totem,
                bornPos = bornPos,
                duration = duration
            };
            rTotem.radius = 0; // 厘米转米
            rTotem.SetPos(bornPos, LVector2.zero, null, false);
            rTotem.AddComponent<BuffComponent>(ETComponentType.BuffComponent);
            rTotem.AddComponent<TotemComponent>(ETComponentType.TotemComponent);
            return rTotem;
        }

        private static Dictionary<int, LFloat> tempDict = new Dictionary<int, LFloat>();

        public static void GetMonsterPanelAttr(int nPower, int nPowerID, IReadOnlyList<int> rMonsterCorr,
            ref List<int> rAttrType, ref List<int> rAttrValue, out int nWeakNum)
        {
            tempDict.Clear();
            var rPowerConfig = TableCenter.monsterPowerType.Get(nPowerID);
            nWeakNum = rPowerConfig.WeakNum;
            var nLen = rPowerConfig.AttType.Count;
            // Power 计算基础值
            for (int k = 0; k < nLen; k++)
            {
                tempDict.TryAdd(rPowerConfig.AttType[k], nPower * new LFloat(true, rPowerConfig.AttValue[k])); // 利用了LFloat自带的万分比转换
            }
            // 计算Corr表修正值
            for (int k = 0; k < rMonsterCorr.Count; k++)
            {
                var rCorrConfig = TableCenter.monsterAttrCorrect.Get(rMonsterCorr[k]);
                for (int j = 0; j < rCorrConfig.AttType.Count; j++)
                {
                    if (tempDict.TryGetValue(rCorrConfig.AttType[j], out var nValue))
                    {
                        tempDict[rCorrConfig.AttType[j]] = nValue * new LFloat(true, rCorrConfig.AttValue[j]);
                    }
                }
            }
            foreach (var rPair in tempDict)
            {
                rAttrType.Add(rPair.Key);
                rAttrValue.Add(rPair.Value.ToInt());
            }
        }

#endregion
    }
}